using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Data;

namespace SIAIllustrator.Calculator
{
    [Serializable]
    public class Illustration
    {

        #region member variables
        ArrayList scenarioList;
        DataTable dtIndex;
        DataTable dtTotal;
        double illustratedTotal = 0;
        double guaranteedTotal = 0;
        double averageIndexRate = 0;
        double averageCreditedRate = 0;
        #endregion

        #region member getters
        public double IllustratedTotal
        {
            get { return illustratedTotal; }
        }

        public double GuaranteedTotal
        {
            get { return guaranteedTotal; }
        }

        public double AverageIndexRate
        {
            get { return averageIndexRate; }
        }

        public double AverageCreditedRate
        {
            get { return averageCreditedRate; }

        }

        public DataTable PayoutTable
        {
            get { return dtTotal; }
        }
        #endregion

        public Illustration()
        { }

        public Illustration(DataTable indexData)
        {
            scenarioList = new ArrayList();
            dtIndex = indexData;

            dtTotal = new DataTable("Payout");
            dtTotal.Columns.Add("#", Type.GetType("System.Int32"));
            dtTotal.Columns.Add("Year", Type.GetType("System.Int32"));
            dtTotal.Columns.Add("Start Balance", Type.GetType("System.Double"));
            dtTotal.Columns.Add("Index", Type.GetType("System.Double"));
            dtTotal.Columns.Add("Credited", Type.GetType("System.Double"));
            dtTotal.Columns.Add("Interest Earned", Type.GetType("System.Double"));
            dtTotal.Columns.Add("Balance", Type.GetType("System.Double"));
            dtTotal.Columns.Add("Payment", Type.GetType("System.Double"));
            dtTotal.Columns.Add("End Balance", Type.GetType("System.Double"));
        }

        public ArrayList ScenarioList
        {
            get { return scenarioList; }
            set { scenarioList = value; }
        }

        public void AddScenario(Scenario scenario)
        {

            if (this.Count != 0)
            {

                int iBeginYear = GetIllustrationBeginYear();


                if (iBeginYear < scenario.BeginYear)
                {

                    scenario.BeginYear = iBeginYear;
                    scenario.EndYear = iBeginYear + (scenario.DeferralPeriod + scenario.PayoutPeriod);

                }
                else if (iBeginYear > scenario.BeginYear)
                {

                    IEnumerator ie = scenarioList.GetEnumerator();

                    while (ie.MoveNext())
                    {
                        Scenario scen = (Scenario)ie.Current;

                        scen.BeginYear = scenario.BeginYear;
                        scen.EndYear = scenario.BeginYear + (scen.DeferralPeriod + scen.PayoutPeriod);
                        scen.Calculate(dtIndex);
                    }
                }
            }

            scenario.Calculate(dtIndex);

            scenarioList.Add(scenario);

            CalculatePayoutTable();

        }

        public int Count
        {
            get { return scenarioList.Count; }
        }

        public int GetIllustrationBeginYear()
        {


            if (scenarioList.Count == 0)
                return -1;
            
            
            IEnumerator ie = scenarioList.GetEnumerator();
            int iBeginYear = int.MaxValue;

            while (ie.MoveNext())
            {
                Scenario s = (Scenario)ie.Current;

                if (s.BeginYear < iBeginYear)
                    iBeginYear = s.BeginYear;

            }

            return iBeginYear;
        }
         
        public Scenario GetScenario(int scenarioID)
        {

            IEnumerator ie = scenarioList.GetEnumerator();
            Scenario returnScen = new Scenario();

            while (ie.MoveNext())
            {

                Scenario scen = (Scenario)ie.Current;
                int id = scen.ScenarioID;

                if (id == scenarioID)
                    returnScen = scen;

            }

            return returnScen;
        }

        public void RemoveScenario(int scenarioID)
        {

            IEnumerator ie = scenarioList.GetEnumerator();
            Scenario removeScen = new Scenario();
            int period = 0;

            while (ie.MoveNext())
            {

                Scenario scen = (Scenario)ie.Current;
                int id = scen.ScenarioID;

                if (id == scenarioID)
                    removeScen = scen;
                else
                    {
                        // determine new beginning year
                        int temp = scen.DeferralPeriod + scen.PayoutPeriod;
                        if (temp > period)
                            period = temp;
                    }

            }

            scenarioList.Remove(removeScen);


            int year = System.DateTime.Now.Year;
            if (System.DateTime.Now.Month == 1)
                year--;


            int newBegin = year - period;


            ie = scenarioList.GetEnumerator();
            while (ie.MoveNext())
            {

                Scenario scen = (Scenario)ie.Current;
                scen.BeginYear = newBegin;
                scen.EndYear = newBegin + (scen.DeferralPeriod + scen.PayoutPeriod);
                scen.Calculate(dtIndex);
            }

            CalculatePayoutTable();
        }

        public void CalculatePayoutTable()
        {
            ArrayList slist = new ArrayList();

            int minYear = int.MaxValue;
            int maxYear = int.MinValue;
            int x = 1;

            /*****************************************
             *  filter scenarioList of Scenario
             *  objects that are disabled
             * ***************************************/

            foreach (Object o in scenarioList)
            {

                Scenario scen = (Scenario)o;

                if (scen.Enabled == true)
                {

                    if (scen.BeginYear < minYear)
                        minYear = scen.BeginYear;

                    if (scen.EndYear > maxYear)
                        maxYear = scen.EndYear;


                    slist.Add(scen);
                }
            }


            /************************************************
             *  combined Payout DataTable member variables
             *  from each Scenario in the filtered list
             ***********************************************/
            
            dtTotal.Rows.Clear();

            if (slist.Count == 0)
                return;

            minYear++;

            while (minYear <= maxYear)
            {

                DataRow r = dtTotal.NewRow();

                r["#"] = x;
                r["Year"] = minYear;


                double sbalance = 0;
                double sp500 = 0;
                double interest = 0;
                double balance = 0;
                double benefit = 0;
                double ebalance = 0;

                foreach (Object o in slist)
                {
                    Scenario s = (Scenario)o;

                    String strSearch = "Year = " + minYear;
                    DataRow[] tempRow = s.Payout.Select(strSearch);

                    if (tempRow.Length != 0)
                    {
                        sbalance += (double)tempRow[0]["Start Balance"];
                        sp500 = (double)tempRow[0]["Index"];
                        interest += (double)tempRow[0]["Interest Earned"];
                        balance += (double)tempRow[0]["Balance"];
                        benefit += (double)tempRow[0]["Payment"];
                        ebalance += (double)tempRow[0]["End Balance"];
                    }
                }

                r["Start Balance"] = sbalance;

                r["Index"] = sp500;

                if (sp500 < 0)
                    r["Credited"] = 0;
                else
                    r["Credited"] = (balance - sbalance) / sbalance;


                r["Interest Earned"] = interest;
                r["Balance"] = balance;
                r["Payment"] = benefit;
                r["End Balance"] = ebalance;

                /********** add row to DataTable ***********/

                dtTotal.Rows.Add(r);


                x++;
                minYear++;

            }


            illustratedTotal = double.Parse(dtTotal.Compute("SUM(Payment)", "").ToString());


            double temp = double.Parse(dtTotal.Compute("SUM(Index)", "").ToString());
            averageIndexRate = temp / dtTotal.Rows.Count;


            double temp2 = double.Parse(dtTotal.Compute("SUM(Credited)", "").ToString());
            averageCreditedRate = temp2 / dtTotal.Rows.Count;

            double guaranteedSubtotal = 0;

            foreach (Object o in slist)
            {

                Scenario scen = (Scenario)o;

                if (scen.Enabled == true)
                {
                    guaranteedSubtotal += IndexCalculator.CalculateGuaranteed(scen.Premium,
                        scen.DeferralPeriod, scen.PayoutPeriod, scen.GuaranteedRate, scen.Type);
                }
            }

            guaranteedTotal = guaranteedSubtotal;


        }

        public void EnableFlip(int scenarioID)
        {

            IEnumerator ie = scenarioList.GetEnumerator();

            while (ie.MoveNext())
            {

                Scenario scen = (Scenario)ie.Current;
                int id = scen.ScenarioID;

                if (id == scenarioID)
                {
                    if (scen.Enabled == true)
                        scen.Enabled = false;
                    else
                        scen.Enabled = true;
                }

            }

            CalculatePayoutTable();
        }

        public void ReplaceScenario(Scenario scenario)
        {

            if (this.Count != 0)
            {

                int iBeginYear = GetIllustrationBeginYear();


                if (iBeginYear < scenario.BeginYear)
                {

                    scenario.BeginYear = iBeginYear;
                    scenario.EndYear = iBeginYear + (scenario.DeferralPeriod + scenario.PayoutPeriod);

                }
                else if (iBeginYear > scenario.BeginYear)
                {

                    IEnumerator ie = scenarioList.GetEnumerator();

                    while (ie.MoveNext())
                    {
                        Scenario scen = (Scenario)ie.Current;

                        scen.BeginYear = scenario.BeginYear;
                        scen.EndYear = scenario.BeginYear + (scen.DeferralPeriod + scen.PayoutPeriod);
                        scen.Calculate(dtIndex);
                    }
                }
            }


            IEnumerator ie2 = scenarioList.GetEnumerator();
            int removeIndex = 0;
            int x = 0;

            while (ie2.MoveNext())
            {
                Scenario scen = (Scenario)ie2.Current;

                if (scen.ScenarioID == scenario.ScenarioID)
                    removeIndex = x;

                x++;
            }

            scenario.Calculate(dtIndex);

            scenarioList.RemoveAt(removeIndex);
            scenarioList.Insert(removeIndex, scenario);

            CalculatePayoutTable();
        }

        private void Shift()
        {
            IEnumerator ie = scenarioList.GetEnumerator();

            while (ie.MoveNext())
            {
                Scenario s = (Scenario)ie.Current;

                if (s.Month < 12)
                    s.Month++;
                else
                {
                    s.Month = 1;
                    s.BeginYear++;
                    s.EndYear++;
                }

                s.Calculate(dtIndex);
            }

        }

        public Illustration BestCaseScenario()
        {

            Illustration best = new Illustration(dtIndex);


            // create a duplicate Illustration object

            IEnumerator ie = scenarioList.GetEnumerator();

            int currMonth = 0;

            while (ie.MoveNext())
            {

                Scenario s = (Scenario)ie.Current;

                currMonth = s.Month;

                Scenario sc = new Scenario(s.Premium, s.Type, 1, 1951, 1951 + (s.EndYear - s.BeginYear), s.DeferralPeriod,
                    s.PayoutPeriod, s.DeferralParticipationRate, s.PayoutParticipationRate, s.GuaranteedRate);

                best.AddScenario(sc);

            }


            int begYear = GetIllustrationBeginYear();
            
            // if begYear = -1 throw exception


            int year = 1951;
            int month = 1;
            int bestMonth = 1;
            int bestYear = 1951;
            double bestTotal = 0;

            best.Shift();

            while ((month <= currMonth) & (year <= begYear))
            {
                
                best.CalculatePayoutTable();

                if (best.IllustratedTotal > bestTotal)
                {
                    bestMonth = month;
                    bestYear = year;
                    bestTotal = best.IllustratedTotal;
                }

                best.Shift();


                if (month < 12)
                {
                    month++;
                }
                else
                {
                    month = 1;
                    year++;
                }
            }


            ie = best.ScenarioList.GetEnumerator();

            while (ie.MoveNext())
            {
                Scenario temp = (Scenario)ie.Current;

                temp.BeginYear = bestYear;
                temp.EndYear = bestYear + (temp.DeferralPeriod + temp.PayoutPeriod);
                temp.Month = bestMonth;

                temp.Calculate(dtIndex);


            }

            best.CalculatePayoutTable();


            return best;

        }

        public Illustration WorstCaseScenario()
        {

            Illustration worst = new Illustration(dtIndex);


            // create a duplicate Illustration object

            IEnumerator ie = scenarioList.GetEnumerator();

            int currMonth = 0;

            while (ie.MoveNext())
            {

                Scenario s = (Scenario)ie.Current;

                currMonth = s.Month;

                Scenario sc = new Scenario(s.Premium, s.Type, 1, 1951, 1951 + (s.EndYear - s.BeginYear), s.DeferralPeriod,
                    s.PayoutPeriod, s.DeferralParticipationRate, s.PayoutParticipationRate, s.GuaranteedRate);

                worst.AddScenario(sc);

            }


            int begYear = GetIllustrationBeginYear();

            // if begYear = -1 throw exception


            int year = 1951;
            int month = 1;
            int worstMonth = 1;
            int worstYear = 1951;
            double worstTotal = double.MaxValue;

            worst.Shift();

            while ((month <= currMonth) & (year <= begYear))
            {

                worst.CalculatePayoutTable();

                if (worst.IllustratedTotal < worstTotal)
                {
                    worstMonth = month;
                    worstYear = year;
                    worstTotal = worst.IllustratedTotal;
                }

                worst.Shift();

                if (month < 12)
                {
                    month++;
                }
                else
                {
                    month = 1;
                    year++;
                }
            }


            ie = worst.ScenarioList.GetEnumerator();

            while (ie.MoveNext())
            {
                Scenario temp = (Scenario)ie.Current;

                temp.BeginYear = worstYear;
                temp.EndYear = worstYear + (temp.DeferralPeriod + temp.PayoutPeriod);
                temp.Month = worstMonth;

                temp.Calculate(dtIndex);


            }

            worst.CalculatePayoutTable();

            return worst;

        }
        
    }

}